//!
/**
 ***********************************************************************************************************
 * <RBEffect implementation>
 ***********************************************************************************************************
 * @file RBEffect.cpp
 ***********************************************************************************************************/

/************************************************************************************************************
 * Includes
 ************************************************************************************************************/
#include "RBEffect.h"

#include "RBMain/RBGeometry.h"
#include "RBMain/RBHardwareBuffer.h"
#include "RBMain/RBRenderer.h"
#include "RBMain/RBMathVector.h"

#include "RBEffectCompiler.h"
#include "RBEffectFactory.h"
#include "RBEffectTechnique.h"
#include "RBEffectVariable.h"

/************************************************************************************************************/

stdext::hash_map<RBString, uint32> RBEffect::ms_mSemantics;
stdext::hash_map<RBString, uint32> RBEffect::ms_mTypes;
RBEffect::RBShaderSamplerStateMap RBEffect::ms_mSamplerStates;
RBEffect::RBRasterizerStateMap RBEffect::ms_mRasterStates;
RBEffect::RBBlendStateMap RBEffect::ms_mBlendStates;
RBEffect::RBDepthStencilStateMap RBEffect::ms_mDepthStencilStates;

static inline stb_int32 stb_hash_number_64(stb_int64 key)
{
    return stb_hash_number(key >> 32) ^ stb_hash_number(key&0xFFFFFFFF);
}

stb_define_hash(stb_i32_dict, stb_i32_dict_, stb_int32, -1, -2, return stb_hash_number(k); , void *);
stb_define_hash(stb_i64_dict, stb_i64_dict_, stb_int64, -1, -2, return stb_hash_number_64(k); , void *);

RBEffect::rb_state_cache RBEffect::ms_cache;

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEffect::~RBEffect()
{
    DestroyTechniques();
}

static inline void add_to_map(const char* key, stdext::hash_map<RBString, uint32>& hash, uint32 value)
{
    RBString key_lower = key;
    std::transform(key_lower.begin(), key_lower.end(), key_lower.begin(), tolower); 

    hash[key_lower] = value;
}

#define DECLARE_SEMANTIC(name) \
    add_to_map(#name, RBEffect::ms_mSemantics, EVS_##name)

#define DECLARE_TYPE(name, type) \
    add_to_map(#name, RBEffect::ms_mTypes, type)

#define add_to_dict(dict, key, value) \
    stb_##key##_dict_add(dict, key, value)

#define DECLARE_BLEND_STATE(blended, srcBlend, dstBlend, blendOp, colorWrites) \
    { RBMaterial material; material.BlendState.IsAlphaBlended = blended; material.BlendState.SrcBlend = srcBlend; material.BlendState.DestBlend = dstBlend; \
    material.BlendState.BlendOp = blendOp; BlendStateKey key = CreateBlendKey(material, colorWrites); \
    stb_i64_dict_add(ms_cache.blend_states, key, RBRenderer::GetSingleton()->GetHBMgr()->CreateBlendState(material, colorWrites)); }

#define DECLARE_RASTER_STATE(wire, cull, frontCCW) \
    { RBMaterial material; material.RasterState.IsWireframe = wire; material.RasterState.Cull = cull; material.RasterState.FrontCounterClockwise = frontCCW; \
      RasterStateKey key = CreateRasterKey(material); \
      stb_i32_dict_add(ms_cache.raster_states, key, RBRenderer::GetSingleton()->GetHBMgr()->CreateRasterizerState(material)); }

#define DECLARE_SAMPLER_STATE(u, v, w, minFilter, magFilter, mipFilter) \
    { RBTextureSampler sampler; sampler.U = u; sampler.V = v; sampler.W = w; \
      sampler.MinFilter = minFilter; sampler.MagFilter = magFilter; sampler.MipFilter = mipFilter; \
      SamplerStateKey key = CreateSamplerKey(sampler); \
      stb_i32_dict_add(ms_cache.sampler_states, key, RBRenderer::GetSingleton()->GetHBMgr()->CreateSamplerState(sampler)); }

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEffect::Init()
{
    DECLARE_SEMANTIC(MATERIAL);
    DECLARE_SEMANTIC(GEOMETRY);
    DECLARE_SEMANTIC(GLOBAL);
    DECLARE_SEMANTIC(MATERIAL);
    DECLARE_SEMANTIC(PROJ);
    DECLARE_SEMANTIC(INVPROJ);
    DECLARE_SEMANTIC(VIEW);
    DECLARE_SEMANTIC(INVVIEW);
    DECLARE_SEMANTIC(WORLD);
    DECLARE_SEMANTIC(INVWORLD);
    DECLARE_SEMANTIC(WORLDVIEW);
    DECLARE_SEMANTIC(INVWORLDVIEW);
    DECLARE_SEMANTIC(VIEWPROJ);
    DECLARE_SEMANTIC(INVVIEWPROJ);
    DECLARE_SEMANTIC(WORLDVIEWPROJ);
    DECLARE_SEMANTIC(INVWORLDVIEWPROJ);
    DECLARE_SEMANTIC(MATERIAL_DIFFUSE);
    DECLARE_SEMANTIC(MATERIAL_AMBIENT);
    DECLARE_SEMANTIC(MATERIAL_SPECULAR);
    DECLARE_SEMANTIC(MATERIAL_EMISSIVE);
    DECLARE_SEMANTIC(MATERIAL_POWER);
    DECLARE_SEMANTIC(MATERIAL_TEXTURE);
    DECLARE_SEMANTIC(EYE_POS);
    DECLARE_SEMANTIC(EYE_DIR);
    DECLARE_SEMANTIC(CAMERA_FRUSTUM);
    DECLARE_SEMANTIC(TIME);
    DECLARE_SEMANTIC(SINTIME);
    DECLARE_SEMANTIC(COSTIME);
    DECLARE_SEMANTIC(TANTIME);
    DECLARE_SEMANTIC(TIME_SINTIME_COSTIME_TANTIME);

    DECLARE_TYPE(bool, EVT_BOOL);
    
    DECLARE_TYPE(int, EVT_INT);
    DECLARE_TYPE(int2, EVT_INT);
    DECLARE_TYPE(int3, EVT_INT);
    DECLARE_TYPE(int4, EVT_INT);
    DECLARE_TYPE(int4x3, EVT_INT);
    DECLARE_TYPE(int4x4, EVT_INT);

    DECLARE_TYPE(float, EVT_FLOAT);
    DECLARE_TYPE(float2, EVT_FLOAT);
    DECLARE_TYPE(float3, EVT_FLOAT);
    DECLARE_TYPE(float4, EVT_FLOAT);
    DECLARE_TYPE(float4x3, EVT_FLOAT);
    DECLARE_TYPE(float4x4, EVT_FLOAT);
    DECLARE_TYPE(string, EVT_STRING);

    DECLARE_TYPE(texture, EVT_TEXTURE);
    DECLARE_TYPE(Texture, EVT_TEXTURE);
    DECLARE_TYPE(texture1D, EVT_TEXTURE1D);
    DECLARE_TYPE(Texture1D, EVT_TEXTURE1D);
    DECLARE_TYPE(texture2D, EVT_TEXTURE2D);
    DECLARE_TYPE(Texture2D, EVT_TEXTURE2D);
    DECLARE_TYPE(texture, EVT_TEXTURE);
    DECLARE_TYPE(Texture3D, EVT_TEXTURE3D);
    DECLARE_TYPE(textureCUBE, EVT_TEXTURECUBE);
    DECLARE_TYPE(TextureCUBE, EVT_TEXTURECUBE);
    
    DECLARE_TYPE(sampler, EVT_SAMPLER);
    DECLARE_TYPE(SamplerState, EVT_SAMPLER);

    DECLARE_BLEND_STATE(false, RBMaterial::AF_ONE, RBMaterial::AF_ZERO, RBMaterial::BO_ADD, 0xFFFFFFFF);
    DECLARE_BLEND_STATE(false, RBMaterial::AF_ONE, RBMaterial::AF_ZERO, RBMaterial::BO_ADD, 0xFFFFFFF0);
    DECLARE_BLEND_STATE(true,  RBMaterial::AF_ONE, RBMaterial::AF_ONE, RBMaterial::BO_ADD, 0xFFFFFFFF);
    DECLARE_BLEND_STATE(true,  RBMaterial::AF_SRC_ALPHA, RBMaterial::AF_INV_SRC_ALPHA, RBMaterial::BO_ADD, 0xFFFFFFFF);
    DECLARE_BLEND_STATE(true,  RBMaterial::AF_ONE, RBMaterial::AF_INV_SRC_ALPHA, RBMaterial::BO_ADD, 0xFFFFFFFF);

    DECLARE_RASTER_STATE(false, RBMaterial::MCM_BACK, false);
    DECLARE_RASTER_STATE(false, RBMaterial::MCM_BACK, true);
    DECLARE_RASTER_STATE(false, RBMaterial::MCM_NONE, true);

    DECLARE_RASTER_STATE(true, RBMaterial::MCM_BACK, false);
    DECLARE_RASTER_STATE(true, RBMaterial::MCM_BACK, true);
    DECLARE_RASTER_STATE(true, RBMaterial::MCM_NONE, false);

    DECLARE_SAMPLER_STATE(RBSamplerState::SAM_CLAMP, RBSamplerState::SAM_CLAMP, RBSamplerState::SAM_CLAMP,
                          RBSamplerState::SFM_POINT, RBSamplerState::SFM_POINT, RBSamplerState::SFM_POINT);

    DECLARE_SAMPLER_STATE(RBSamplerState::SAM_CLAMP, RBSamplerState::SAM_CLAMP, RBSamplerState::SAM_CLAMP,
                          RBSamplerState::SFM_LINEAR, RBSamplerState::SFM_LINEAR, RBSamplerState::SFM_LINEAR);

    DECLARE_SAMPLER_STATE(RBSamplerState::SAM_WRAP, RBSamplerState::SAM_WRAP, RBSamplerState::SAM_WRAP,
                          RBSamplerState::SFM_POINT, RBSamplerState::SFM_POINT, RBSamplerState::SFM_POINT);

    DECLARE_SAMPLER_STATE(RBSamplerState::SAM_WRAP, RBSamplerState::SAM_WRAP, RBSamplerState::SAM_WRAP,
                          RBSamplerState::SFM_LINEAR, RBSamplerState::SFM_LINEAR, RBSamplerState::SFM_LINEAR);

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEffectTechnique* RBEffect::GetTechnique( uint32 uiTechnique )
{
    return m_vTechniques[uiTechnique];
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
uint32 RBEffect::GetTechniqueByName( const RBString& strName )
{
    std::vector<RBString>::iterator iter = std::find(m_vTechniqueNames.begin(), m_vTechniqueNames.end(), strName);
    if (iter != m_vTechniqueNames.end())
    {
        uint32 uiTechnique = iter - m_vTechniqueNames.begin();
        return uiTechnique;
    }
    return UINT_MAX;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEffect::AddTechnique( const RBString& strName, RBEffectTechnique* pTechnique )
{
    m_vTechniqueNames.push_back(strName);
    m_vTechniques.push_back(pTechnique);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEffect::DestroyTechniques()
{
    m_vTechniqueNames.clear();
    for (uint32 ui=0; ui < m_vTechniques.size(); ++ui)
    {
        delete m_vTechniques[ui];
    }
    m_vTechniques.clear();
    m_vEffectCBVariables.clear();
    m_vEffectRVVariables.clear();
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEffect::BuildEffectBuffer(RBRenderer* pRenderer, const RBGeometry* pGeometry, const RBMaterial* pMaterial,
                                 void* pBufferData, const RBShaderResourceView** ppViews, 
                                 const RBShaderSamplerState** ppSamplers,
                                 const RBEffectFactory* pEffectFactory) const
{
    PROFILE_FUNC();  

    float4x4 mat;
    char* pDstData = (char*)pBufferData;
    uint32 uiCount;
    for (uint32 ui=0; ui < m_vEffectCBVariables.size(); ++ui)
    {
        const void* pSrcData=0;
        const RBEffectCBVariable& effectVar = m_vEffectCBVariables[ui];
        switch(effectVar.Semantic)
        {
        case EVS_VIEW:
            mat = (pRenderer->GetView()).Transpose();
            pSrcData = &mat;
            break;
        case EVS_INVVIEW:
            mat = (RBMath::InvertMatrix(pRenderer->GetView())).Transpose();
            pSrcData = &mat;
            break;
        case EVS_WORLD:
            mat = (RBMath::Build(pGeometry->WorldXForm())).Transpose();
            pSrcData = &mat;
            break;
        case EVS_WORLDVIEW:
            mat = (pRenderer->GetView() * RBMath::Build(pGeometry->WorldXForm())).Transpose();
            pSrcData = &mat;
            break;
        case EVS_VIEWPROJ:
            mat = (pRenderer->GetProj() * pRenderer->GetView()).Transpose();
            pSrcData = &mat;
            break;
        case EVS_INVVIEWPROJ:
            mat = (RBMath::InvertMatrix(pRenderer->GetProj() * pRenderer->GetView())).Transpose();
            pSrcData = &mat;
            break;
        case EVS_WORLDVIEWPROJ:
            mat = (pRenderer->GetProj() * pRenderer->GetView() * RBMath::Build(pGeometry->WorldXForm())).Transpose();
            pSrcData = &mat;
            break;
        case EVS_MATERIAL_AMBIENT:
            pSrcData = &pMaterial->Ambient.r;
            break;
        case EVS_MATERIAL_DIFFUSE:
            pSrcData = &pMaterial->Diffuse.r;
            break;
        case EVS_MATERIAL_SPECULAR:
            pSrcData = &pMaterial->Specular.r;
            break;
        case EVS_MATERIAL_EMISSIVE:
            pSrcData = &pMaterial->Emissive.r;
            break;
        default:
            pGeometry->DataContainer.get_effect_data_blob(effectVar.Name, &pSrcData, &uiCount);
            if (pSrcData != NULL) break;
            pMaterial->DataContainer.get_effect_data_blob(effectVar.Name, &pSrcData, &uiCount);
            if (pSrcData != NULL) break;
            pEffectFactory->DataContainer.get_effect_data_blob(effectVar.Name, &pSrcData, &uiCount);
            if (pSrcData != NULL) break;
            break;
        }
        if (pSrcData)
        {
            memcpy(pDstData+effectVar.Offset, pSrcData, effectVar.Size);
        }
        else
        {
            memset(pDstData+effectVar.Offset,0,effectVar.Size);
        }
    }

    for (uint32 ui=0; ui < m_vEffectRVVariables.size(); ++ui)
    {
        const RBEffectRVVariable& effectVar = m_vEffectRVVariables[ui];
        switch(effectVar.Semantic)
        {
        default:
        case EVS_MATERIAL:
            ppViews[ui] = pMaterial->DataContainer.GetView(effectVar.Name);
            if (ppViews[ui] != NULL) break;
        case EVS_GEOMETRY:
            ppViews[ui] = pGeometry->DataContainer.GetView(effectVar.Name);
            if (ppViews[ui] != NULL) break;
        case EVS_GLOBAL:
            ppViews[ui] = pEffectFactory->DataContainer.GetView(effectVar.Name);
            if (ppViews[ui] != NULL) break;
        }
    }

    for (uint32 ui=0; ui < m_vEffectSamplerVariables.size(); ++ui)
    {
        const RBEffectSamplerVariable& effectVar = m_vEffectSamplerVariables[ui];
        const RBTextureSampler* pSampler = NULL;
        switch(effectVar.Semantic)
        {
        default:
        case EVS_MATERIAL:
            pSampler = pMaterial->DataContainer.RetrieveSampler(effectVar.Name);
            if (pSampler != NULL) break;
        case EVS_GEOMETRY:
            pSampler = pGeometry->DataContainer.RetrieveSampler(effectVar.Name);
            if (pSampler != NULL) break;
        case EVS_GLOBAL:
            pSampler = pEffectFactory->DataContainer.RetrieveSampler(effectVar.Name);
            if (pSampler != NULL) break;
        }
     
        ppSamplers[ui] = (pSampler != NULL) ? FindSamplerState(*pSampler) : NULL;
    }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
uint32 RBEffect::GetSemantic( const RBString& strName )
{
    stdext::hash_map<RBString, uint32>::iterator iter = ms_mSemantics.find(strName);
    return iter != ms_mSemantics.end() ? iter->second : -1;
}

uint32 RBEffect::GetType( const RBString& strType )
{
    stdext::hash_map<RBString, uint32>::iterator iter = ms_mTypes.find(strType);
    return iter != ms_mTypes.end() ? iter->second : -1;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEffect::SetEffectVariables( const std::vector<RBEffectCBVariable>& vEffectCBVariables, 
                                   const std::vector<RBEffectRVVariable>& vEffectRVVariables,
                                   const std::vector<RBEffectSamplerVariable>& vEffectSamplersVariables)
{
    m_vEffectCBVariables = vEffectCBVariables;
    m_vEffectRVVariables = vEffectRVVariables;
    m_vEffectSamplerVariables = vEffectSamplersVariables;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBShaderSamplerState* RBEffect::FindSamplerState( const RBTextureSampler& sampler )
{
    PROFILE_FUNC();
    RBShaderSamplerState* res;
    return stb_i32_dict_get_flag(ms_cache.sampler_states, CreateSamplerKey(sampler), (void**)&res) ? res : NULL;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBRasterizerState* RBEffect::FindRasterState( const RBMaterial& material )
{
    PROFILE_FUNC();
    RBRasterizerState* res;
    return (stb_i32_dict_get_flag(ms_cache.raster_states, CreateRasterKey(material), (void**)&res)) ? res : NULL;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBBlendState* RBEffect::FindBlendState( const RBMaterial& material, uint32 colorWrites )
{
    PROFILE_FUNC();
    RBBlendState* res;
    return stb_i64_dict_get_flag(ms_cache.blend_states, CreateBlendKey(material, colorWrites), (void**)&res) ? res : NULL;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBDepthStencilState* RBEffect::FindDepthStencilState( const RBMaterial& material )
{
    PROFILE_FUNC();
    RBEffect::DepthStencilStateKey key = CreateDepthStencilKey(material);
    RBDepthStencilState* depth_stencil_state = NULL;
    if (!stb_i64_dict_get_flag(ms_cache.depth_stencil_states, key, (void**)&depth_stencil_state))
    {
        RBDepthStencilState* pState = RBRenderer::GetSingleton()->GetHBMgr()->CreateDepthStencilState(material);
        stb_i64_dict_add(ms_cache.depth_stencil_states, key, depth_stencil_state);
        return pState;
    }
    return depth_stencil_state;
}

#define GetBitfield(value, mask, size, offset) \
    (value & mask) << offset; offset += size

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEffect::SamplerStateKey RBEffect::CreateSamplerKey( const RBTextureSampler& sampler )
{
    RBEffect::SamplerStateKey key = 0;
    uint32 offset = 0;
    key |= GetBitfield(sampler.U, RBSamplerState::SAM_MASK, RBSamplerState::ms_maxAdressMode, offset);
    key |= GetBitfield(sampler.V, RBSamplerState::SAM_MASK, RBSamplerState::ms_maxAdressMode, offset);
    key |= GetBitfield(sampler.W, RBSamplerState::SAM_MASK, RBSamplerState::ms_maxAdressMode, offset);
    key |= GetBitfield(sampler.MinFilter, RBSamplerState::SFM_MASK, RBSamplerState::ms_maxFilterMode, offset);
    key |= GetBitfield(sampler.MagFilter, RBSamplerState::SFM_MASK, RBSamplerState::ms_maxFilterMode, offset);
    key |= GetBitfield(sampler.MipFilter, RBSamplerState::SFM_MASK, RBSamplerState::ms_maxFilterMode, offset);

    return key;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEffect::RasterStateKey RBEffect::CreateRasterKey( const RBMaterial& material )
{
    return material.RasterState.Mask;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEffect::BlendStateKey RBEffect::CreateBlendKey( const RBMaterial& material, uint32 colorWrites )
{
    uint64 key = colorWrites;
    key = (key << 32) | material.BlendState.Mask;
    return key;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEffect::DepthStencilStateKey RBEffect::CreateDepthStencilKey( const RBMaterial& material )
{
    return material.DepthState.Mask;
}

uint32 RBEffect::get_semantic_from_name( const RBString& strName )
{
    RBString name_lower = strName;
    std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), tolower); 
    stdext::hash_map<RBString, uint32>::iterator iter = ms_mSemantics.find(name_lower);
    return iter != ms_mSemantics.end() ? iter->second : -1;

}

RBEffect::rb_state_cache::rb_state_cache()
{
    sampler_states = stb_i32_dict_create();
    raster_states = stb_i32_dict_create();
    blend_states = stb_i64_dict_create();
    depth_stencil_states = stb_i64_dict_create();
}

RBEffect::rb_state_cache::~rb_state_cache()
{
    stb_i32_dict_destroy(sampler_states);
    stb_i32_dict_destroy(raster_states);
    stb_i64_dict_destroy(blend_states);
    stb_i64_dict_destroy(depth_stencil_states);
}
